home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 20 / Cream of the Crop 20 (Terry Blount) (1996).iso / program / recio214.zip / TUTOR.TXT < prev    next >
Text File  |  1996-06-14  |  17KB  |  458 lines

  1.     Title: A TUTORIAL INTRODUCTION TO THE C LANGUAGE RECIO LIBRARY
  2. Copyright: (C) 1994-1996, William Pierpoint
  3.   Version: 2.14
  4.      Date: June 14, 1996
  5.  
  6.  
  7.  
  8. 1.0 STDIO AND RECIO
  9.  
  10. The program many people learned when first introduced to the C programming
  11. language was the "hello, world" program published in Kernighan and Richie's
  12. "The C Programming Language."  And the first line of that first program,
  13.  
  14. #include <stdio.h>
  15.  
  16. tells the compiler that the functions and macros provided by the standard
  17. input/output library are needed for the program.  The "hello, world" program
  18. uses the powerful printf statement for output.  The counterpart for input,
  19. scanf, looks deceptively like printf, but unfortunately has many ways to
  20. trap an unwary programmer.  These include failure to provide the address
  21. of an variable, size of argument mismatched with the specification in the
  22. format statement, and number of arguments mismatched with the specification
  23. in the format statement.
  24.  
  25. Suppose you use a library that defines a boolean type as an unsigned
  26. character.  You develop an output module that writes variables of type
  27. boolean to a file,
  28.  
  29.     /* output */
  30.     boolean state=0;
  31.     ...
  32.     fprintf(fp, "%6d", state);
  33.  
  34. where fp is a pointer to FILE.  Once you get the output module working, you
  35. decide to develop the input module to read back into the program the data
  36. you wrote to disk.
  37.  
  38.     /* input */
  39.     boolean state;
  40.     ...
  41.     fscanf(fp, "%d", &state);
  42.  
  43. So, is this ok?  On one compiler this worked consistently without problems,
  44. but on another compiler, it overwrote the value in another variable.  Why?
  45. Because fscanf expects the address of an integer, not an unsigned char.
  46. One compiler overwrote the adjoining memory address and the other compiler
  47. apparently did not.  And since compilers don't do type checking on functions
  48. with variable number of arguments, you don't get any errors or warnings.  That
  49. is what is so infuriating about this type of error.  You see that another
  50. variable has the wrong value, you check all the code that uses the other
  51. variable, and you can't find anything wrong with it.  In the midst of
  52. development, it is hard to imagine that the problem is caused by code that
  53. has nothing to do with the variable containing the bad value.
  54.  
  55. The recio (record input/output) library takes a different approach to input.
  56. To input the boolean variable using the recio library, just write
  57.  
  58.     /* input */
  59.     boolean state;
  60.     ...
  61.     state = rgeti(rp);
  62.  
  63. where rp is a pointer to REC (the recio structure analogous to the stdio 
  64. FILE structure).  The rgeti function gets an integer from the input and the 
  65. compiler converts it to a boolean when it makes the assignment.  No need to 
  66. worry about crazy pointers here!
  67.  
  68. Since virtually every program has to do input or output, the stdio library
  69. is very familiar to C programmers.  Many functions in the recio library
  70. are analogous to the stdio library.  This makes the learning curve easier.
  71.  
  72.         Analogous stdio/recio components
  73.  
  74.     stdio        recio
  75.     ---------    ---------
  76.     FILE        REC
  77.     FOPEN_MAX    ROPEN_MAX
  78.  
  79.     stdin        recin
  80.     stdout        recout
  81.     stderr        recerr
  82.     stdprn        recprn
  83.  
  84.     fopen        ropen
  85.     fclose        rclose
  86.     fgets        rgetrec
  87.     fscanf        rgeti, rgetd, rgets, ...
  88.     fprintf         rputi, rputd, rputs, ...
  89.     clearerr    rclearerr
  90.     fgetpos        rgetfldpos
  91.     fsetpos        rsetfldpos
  92.     feof        reof
  93.     ferror        rerror
  94.  
  95.  
  96. 2.0 EXAMPLES
  97.  
  98. 2.1 Line Input
  99.  
  100. One of the first things you can do with the recio library to is to substitute
  101. rgetrec() for fgets() to get a line of text (record) from a file (or standard
  102. input).  The advantage of rgetrec() is that you don't have to go to the
  103. trouble to allocate space for a string buffer, or worry about the size of the
  104. string buffer.  The recio library handles that for you automatically.  The
  105. rgetrec function is like fgets() in that it gets a string from a stream, but
  106. it is like gets() in that it trims off the trailing newline character.
  107.  
  108. The echo program demonstrates the use of the rgetrec function.
  109.  
  110. /* echo.c - echo input to output */
  111.  
  112. #include <stdio.h>
  113. #include <stdlib.h>
  114.  
  115. #include "recio.h"
  116.  
  117. main()
  118. {
  119.     /* while input continues to be available */
  120.     while (rgetrec(recin)) {
  121.  
  122.         /* echo record buffer to output */
  123.         puts(rrecs(recin));
  124.     }
  125.  
  126.     /* if exited loop before end-of-file */
  127.     if (!reof(recin)) {
  128.         exit (EXIT_FAILURE);
  129.     }
  130.     return (EXIT_SUCCESS);
  131. }
  132.  
  133. The echo program reads standard input using recin, the recio equivalent to
  134. stdin.  For output the recio library provides recout, recerr, and recprn.
  135.  
  136. The rgetrec function returns a pointer to the record buffer, but the echo
  137. program did not use a variable to hold a pointer to the string (although
  138. it could have).  Instead, the record buffer was accessed through the rrecs
  139. macro, which provides a pointer to the record buffer.
  140.  
  141. Since rgetrec returns NULL on either error or end-of-file, your program
  142. needs to find out which condition occurred.  You can use either the reof
  143. function or the rerror function to determine this.  The echo program uses
  144. the reof function; the wc program in section 2.2 uses the rerror function.
  145. The echo program just exits with a failure status if an error occurred
  146. before the end of the file was reached.
  147.  
  148.  
  149. 2.2 Line, Word, and Character Counting
  150.  
  151. The power of the recio library comes from its facilities to break records
  152. into fields and from the many functions that operate on fields.  Because
  153. the default field delimiter is the space character (which breaks on any
  154. whitespace), the default behavior is equivalent to subdividing a line of
  155. text into words.
  156.  
  157. The wc program counts lines, words, and characters for files specified
  158. on the command line.
  159.  
  160. /* wc.c - count lines, words, characters */
  161.  
  162. #include <stdio.h>
  163. #include <stdlib.h>
  164. #include <string.h>
  165.  
  166. #include "recio.h"
  167.  
  168. main(int argc, char *argv[])
  169. {
  170.     int  nf;    /* number of files */
  171.     REC *rp;    /* pointer to open record stream */
  172.     long nc,    /* number of characters (not including line terminator) */
  173.          nw,    /* number of words */
  174.          nl;    /* number of lines */
  175.  
  176.     /* loop through all files */
  177.     for (nf=1; nf < argc; nf++) {
  178.  
  179.         /* open record stream */
  180.         rp = ropen(argv[nf], "r");
  181.         if (!rp) {
  182.             if (errno == ENOENT) {
  183.                 printf("ERROR: Could not open %s\n", argv[nf]);
  184.                 continue;
  185.             } else {
  186.                 printf("FATAL ERROR: %s\n", strerror(errno));
  187.                 exit (EXIT_FAILURE);
  188.             }
  189.         }
  190.  
  191.         /* initialize */
  192.         nc = nw = 0;
  193.         rsetfldch(rp, ' ');
  194.         rsettxtch(rp, ' ');
  195.  
  196.         /* loop through all lines (records) */
  197.         while (rgetrec(rp)) {
  198.  
  199.             /* count number of characters in line w/o '\n' */
  200.             nc += strlen(rrecs(rp));
  201.  
  202.             /* count number of words (fields) */
  203.             nw += rnumfld(rp);
  204.         }
  205.  
  206.         /* if exited loop on error rather than end-of-file */
  207.         if (rerror(rp)) {
  208.             printf("ERROR reading %s - %s\n", 
  209.              rnames(rp), rerrstr(rp));
  210.             exit (EXIT_FAILURE);
  211.         }
  212.  
  213.         /* get number of lines (records) */
  214.         nl = rrecno(rp);
  215.  
  216.         /* output results */
  217.         printf("%s: %ld %ld %ld\n", rnames(rp), nl, nw, nc);
  218.  
  219.         /* close record stream */
  220.         rclose(rp);
  221.     }
  222.     return (EXIT_SUCCESS);
  223. }
  224.  
  225. If ropen() fails, the wc program goes to the trouble to check errno for
  226. ENOENT rather than just assuming that the failure was caused by a missing
  227. file.
  228.  
  229. The wc program also sets the field and text delimiters even though it is
  230. unneccessary here since they are the same as the default values.  If you
  231. wanted to read a comma-delimited file, you could set the the delimiters to
  232.  
  233.     rsetfldch(rp, ',');
  234.     rsettxtch(rp, '"');
  235.  
  236. which allows you to also read text fields containing commas by delimiting
  237. the text with quotes, such as "Hello, World."
  238.  
  239. Fields are counted using the rnumfld function, which counts all the fields 
  240. in the current record.  In re